package com.lnsystem.service.impl;

import java.util.List;


















import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.lnsystem.constants.LNSystemConstants;
import com.lnsystem.dao.AnalyticsEventDao;
import com.lnsystem.dao.StockDao;
import com.lnsystem.model.AnalyticsEvent;
import com.lnsystem.model.MovingAverage;
import com.lnsystem.model.Stock;
import com.lnsystem.model.StockDailyData;
import com.lnsystem.model.StockData;
import com.lnsystem.model.StockDataType;
import com.lnsystem.model.StockWeeklyData;
import com.lnsystem.service.QuoteService;
import com.lnsystem.service.ScheduledTask;
import com.lnsystem.service.SymbolsService;

@Service
public class ScheduledTaskImpl implements ScheduledTask {
	
	Logger logger = Logger.getLogger(ScheduledTask.class);

	@Autowired
	QuoteService quoteService;
	
	@Autowired
	SymbolsService symbolsService;
	
	@Autowired
	StockDao stockDao;
	
	@Autowired
	AnalyticsEventDao analyticsEventDao;
	
	List<String> allSymbols;
	private double runningSum; // TODO find a good place for this later.

	
	public ScheduledTaskImpl() {
		super();
	}


	
    /**
     * {@inheritDoc} 
     */
	@Override
	public void updateSymbols() {
		if(allSymbols == null) {
			allSymbols = symbolsService.getAllSymbols(false);
		}
	}

    /**
     * {@inheritDoc} 
     */
	@Override
	@Transactional
	public void downloadDailyData() {
		int id;
		
		long startTime = System.currentTimeMillis();
		
		//get today's data for all stocks in the  symbol files.
		List<StockDailyData> todaysDataList = quoteService.getDailyDataForStocks();
		
		if(todaysDataList!=null && todaysDataList.size()>0) {
			for(StockDailyData todaysData : todaysDataList) {
				String symbol = todaysData.getSymbol();
				Stock stock = stockDao.getStockBySymbol(symbol);
				if(stock != null) { //stock exists in our database. so just update today's data and recalculate MA values.
					
					
					//TODO check to see if dailydata is already there and if so just update it..
					
					
					todaysData.setStockId(stock);
					
					logger.info("Getting today' data for :"+symbol+"...");
					List<StockDailyData> dailyDataList = stock.getStockDailyData();
					
					//TODO: add only if today's data has not been added already...
					dailyDataList.add(todaysData);
					//calculateSMA(dailyDataList,10);
					//calculateSMA(dailyDataList,50);
					//calculateSMA(dailyDataList,200);
					//calculateEMA(dailyDataList,10);
					//calculateEMA(dailyDataList,8);
					
					
					updatePriceAction(todaysData);
					
					//check if stock is in analyticsevent table and update current price...
					List<AnalyticsEvent> eventList = analyticsEventDao.getAnalyticsEventsForStock(stock);
					if(eventList != null && eventList.size()>0) {
						for(AnalyticsEvent event: eventList) {
							if(event.getPriceOnEvent() == null) {
								event.setPriceOnEvent(todaysData.getClose());
							}
							logger.info(">>> Updating current price, change, percentage change in the ANALYTICSEVENT table for :"+stock.getSymbol());
							event.setCurrentPrice(todaysData.getClose());
							event.setPriceChangeSinceEvent(todaysData.getClose() - event.getPriceOnEvent());
							event.setPricePercentChangeSinceEvent(((todaysData.getClose() - event.getPriceOnEvent())/event.getPriceOnEvent())*100);
						}
					}

					
					
					//persist the data now..
					id = stockDao.addStock(stock);
					

					
					if(id>0) {
						logger.debug(">>>>>>>>>Stock "+stock.getSymbol()+" added/updated to the database successfully, id:"+id);
					} else {
						logger.debug("!!!! Stock "+stock.getSymbol()+" not added to the database, it probably already exists!");
					}
				} else {
					//this means the stock does not exist in our database.
					//so download the historical data for this stock.
					logger.info("Stock :"+symbol+" does not exist, downloading historical data...");
					
					DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
					DateTime today = new DateTime();
					String startDate = formatter.print(today.minusDays(365)); // get data for the last 1 year
					String endDate = formatter.print(today);
					
					downloadHistoricalData(symbol, startDate, endDate, StockDataType.DAILY);
				}
				
			}
			
			
		}		
		long endTime = System.currentTimeMillis();
		logger.info("Time taken to download stock data (today's/historical) :"+(endTime-startTime)+" ms.");
/*		
		updateSymbols();
		int id;
		for(String s : allSymbols){
			logger.info("Now getting stock data for symbol:"+s);
			
			Stock stock = stockDao.getStockBySymbol(s);
			if(stock != null) { // stock exists in our database already. so just today's data needs to be downloaded.
				StockDailyData todaysData = quoteService.getStockDailyData(s);
				todaysData.setStockId(stock);
				List<StockDailyData> dailyDataList = stock.getStockDailyData();
				if(dailyDataList != null) {
					dailyDataList.add(todaysData);
				}
				
				//TODO - calculating SMA, EMA for all dailydata will take time as the number of dailydata records increase.
				// so some logic needs to be added to process only last 1 year's worth of data..
				
				
				// calculate moving average
				calculateSMA(dailyDataList,10);
				calculateSMA(dailyDataList,50);
				calculateSMA(dailyDataList,200);
				calculateEMA(dailyDataList,10);
				calculateEMA(dailyDataList,8);
				//calculateBB(dailyDataList, 20, 1);			
				
				//persist the data now..
				id = stockDao.addStock(stock);
				if(id>0) {
					logger.info(">>>>>>>>>Stock "+s+" added to the database successfully, id:"+id);
				} else {
					logger.info("!!!! Stock "+s+" not added to the database, it probably already exists!");
				}
				
			} else { // this must be a new stock (new IPO)
				
				downloadHistoricalDailyData(s);
				
			}
		}
		
		*/
		
	}
	
	
    /**
     * {@inheritDoc} 
     */
	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void downloadHistoricalData(StockDataType dataType, String startDate, String endDate) {
		
		long startTime = System.currentTimeMillis();
		
		List<StockDailyData> stockDailyList = null;
		List<StockWeeklyData> stockWeeklyList = null;
		Stock stock = null;
		
		
		try {
					//1. get the list of StockDaily records 
					// for now do this by using QuoteServiceTKImpl->getHistoricalQuotes()
					// later it needs to be done using daoimpl methods
				
				// get the latest list of symbols
				updateSymbols();
				if(allSymbols!=null){
					logger.debug("Total symbols downloaded:"+allSymbols.size());
				}
		

				DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
				DateTime today = new DateTime();
				if(startDate == null){
					if (dataType == StockDataType.DAILY){
						startDate = formatter.print(today.minusDays(365)); // get data for the last 1 year
					} else if (dataType == StockDataType.WEEKLY){
						startDate = formatter.print(today.minusDays(365*5));// get data for the last 5 years
					}
				}
				if(endDate == null){
					endDate = formatter.print(today);
				}
				
				
				for(String s : allSymbols){
					
					
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
					// call download historical data for specific symbol.....
					downloadHistoricalData(s,startDate, endDate, dataType);		
					
				}
				
				long endTime = System.currentTimeMillis();
				logger.info("getHistoricalData() took " + (endTime - startTime) + " milliseconds");
					
		} catch (Exception e) {
			//e.printStackTrace();
			logger.error(ExceptionUtils.getStackTrace(e));
		}
		
	}
	
    /**
     * {@inheritDoc} 
     */
	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void downloadHistoricalData(String s, String startDate, String endDate, StockDataType dataType) {
		
		long startTime = System.currentTimeMillis();
		

		List<StockDailyData> stockDailyList = null;
		List<StockWeeklyData> stockWeeklyList = null;
		Stock stock = null;
			
		logger.info("Now getting historical data for symbol:"+s);
			
		
		// check if the stock already exists in the database.	
		stock = stockDao.getStockBySymbol(s);
		
		if(stock != null){
			//if stock already exists, get all dailydata/weeklydata.
			if(dataType == StockDataType.DAILY) {
				//check if dailydata is available already in the database for the given start & end dates.
				stockDailyList = stockDao.getStockDailyData(stock, new LocalDate(startDate), new LocalDate(endDate), false);
			} else if (dataType == StockDataType.WEEKLY) {
				//TODO needs to add more logic to check if weekly data is available for the given dates instead of just getStockWeeklyData()
				stockWeeklyList = stock.getStockWeeklyData();
			}
		} else {
			stock = quoteService.getStockData(s);
		}

		if(stock!=null){
			if(dataType == StockDataType.DAILY) {
				
				if(stockDailyList == null || stockDailyList.size() == 0) { // daily data is not available locally.
					stockDailyList = quoteService.getHistoricalQuotes(s, startDate, endDate, dataType);
					
					if(stockDailyList!=null && stockDailyList.size()>0){
						
						//TODO - see if there is any other efficient way to do this...
						for(StockDailyData sd : stockDailyList) {
							sd.setStockId(stock);
						}
						stock.setStockDailyData(stockDailyList);
						
						
						if(stockDailyList!=null && stockDailyList.size() == 1) {
							updatePriceAction(stockDailyList.get(0));
						} else {
							updatePriceAction(stock, stockDailyList);
						}

						//persist the data now..
						stockDao.addStock(stock);
						
					} else {
						logger.debug(">>> stockdaily data is either null or empty for symbol:"+s );
					}	
				} else {
					logger.debug(">>> Daily data for "+s+" already exists in the database");
				}
	
			} else if (dataType == StockDataType.WEEKLY) {
				
				if(stockWeeklyList == null){
					stockWeeklyList = quoteService.getHistoricalQuotes(s, startDate, endDate,dataType);
					
					if(stockWeeklyList!=null && stockWeeklyList.size()>0){
						//TODO - see if there is any other efficient way to do this...
					
						logger.debug(">>>>Size of stockWeeklyList : "+stockWeeklyList.size());
						
						for(StockWeeklyData sw : stockWeeklyList) {
							sw.setStockId(stock);
						}
						stock.setStockWeeklyData(stockWeeklyList);

						//persist the data now..
						stockDao.addStock(stock);
						
						//TODO create updatePriceAction for weekly data as well.
						
					} else {
						logger.debug(">>> stockweekly data is either null or empty for symbol:"+s );
					}			
				} else {
					logger.debug(">>> Weekly data for "+s+" already exists in the database");
				}
			}				
			
			
		} else {
			logger.debug(">>> quoteService.getStockData() returned null for symbol:"+s );
		}
	}			
	

/*	@Override
	public void getDailyData() {
		
		// get the latest list of symbols
		updateSymbols();
		if(allSymbols!=null){
			logger.debug("Total symbols downloaded:"+allSymbols.size());
		}
		
		DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
		DateTime today = new DateTime();
		String endDate = formatter.print(today);
		String startDate = formatter.print(today.minusDays(365));
		
		for(String s : allSymbols){
			
			logger.info("Now getting historical data for symbol:"+s);
			StockDailyData stockDailyData = quoteService.getStockDailyData(s);
			
			// now calculate tech indicators (SMA, EMA, BB etc)
			// use a dao method that takes symbol-s, startDate & endDate, and gives the list<StockDailyData> for s.
			// pass this list to calculateSMA, EMA, BB etc..
		}
	}*/
	

    /**
     * {@inheritDoc} 
     */
	@Override
	@Transactional
	public String updatePriceAction(StockDailyData today) {
		String priceActionString = "";
		LocalDate yesterdaysDate;
		double open;
		double close;
		int daysCount = 0;
		
		LocalDate todaysDate = new LocalDate(today.getDate());
		if(todaysDate.minusDays(1).getDayOfWeek() == DateTimeConstants.SUNDAY) { // today is monday, so yesterday should be set to Friday.
			yesterdaysDate = todaysDate.minusDays(3);
		} else {
			yesterdaysDate = todaysDate.minusDays(1);
		}
		
		StockDailyData yesterday = stockDao.getStockDailyData(today.getSymbol(), yesterdaysDate);
		
		if(yesterday == null) { // yesterday might be null on weekdays if it was a holiday.

			while(yesterday == null && (daysCount < 15)) { // for now looping until a non-null record is found.
				logger.error(">>>>>>>>> Could not get yesterday's data while updating today's price action!!"
						+ "... StockDao.getStockDailyData() for symbol:"+today.getSymbol()+" and date:["+yesterdaysDate.toDate()+"] (day="+yesterdaysDate.getDayOfWeek()+" (6=Sat,7=Sun)) returned null.");
				yesterdaysDate = yesterdaysDate.minusDays(1);
				yesterday = stockDao.getStockDailyData(today.getSymbol(), yesterdaysDate);
				daysCount++;
			}
		}
		
		if(yesterday != null) {
			
			open = today.getOpen();
			//high = today.getHigh();
			//low = today.getLow();
			close = today.getClose();
			
			if( (close > open) && (close > yesterday.getClose())){ //today's open> today's close && today's close > y'days close.
				today.setPriceAction("G"); //green day
			} else if ((close > open) && (close < yesterday.getClose())){ //today's open> today's close && today's close > y'days close.
				today.setPriceAction("H"); //red hollow candle reversal day 
			} else if( (close < open) && (close > yesterday.getClose())) {
				today.setPriceAction("B"); //black or solid candle day as stock closed lower than opening price but above yesterday's close. 
			} else if ((close < open) && (close < yesterday.getClose()) ) {
				today.setPriceAction("R"); //red day
			}
			
			if( (close == open) && (close > yesterday.getClose())){ //today's open> today's close && today's close > y'days close.
				today.setPriceAction("G"); //green indecision day
			} else if ((close == open) && (close < yesterday.getClose())){ //today's open> today's close && today's close > y'days close.
				today.setPriceAction("R"); //red indecision day 
			} else if( (close > open) && (close == yesterday.getClose())) {
				today.setPriceAction("G"); //Green day as stock closed lower than opening price but above yesterday's close. 
			} else if ((close < open) && (close == yesterday.getClose()) ) {
				today.setPriceAction("R"); //red day
			}	else if ((close == open) && (close == yesterday.getClose()) ) {
				today.setPriceAction("G"); //neutral/green day
			}
			
			priceActionString = today.getPriceAction();
			
		} else {
			logger.error(">>>>>>>>> Could not get yesterday's data while updating today's price action!!"
					+ "... StockDao.getStockDailyData() for symbol:"+today.getSymbol()+" and date:["+yesterdaysDate.toDate()+"] returned null.");
		}
		
		
		stockDao.flushAndClear();
		
		return priceActionString;
	}
	
	
    /**
     * {@inheritDoc} 
     */
	@Transactional
	@Override
	public void updatePriceAction(Stock stock, List<StockDailyData> dailyDataList) {
		double open;
		double close;
		//double high;
		//double low;
		StockDailyData today;
		StockDailyData yesterday = null;
		
		logger.info(">>>>> Now updating price action info for :"+stock.getSymbol()+"...");
		
		//List<Stock> allStocks = stockDao.getAllStocks();
		//for(Stock stock : allStocks) {
		if(dailyDataList == null) {
			dailyDataList = stockDao.getStockDailyData(stock, true);
			java.util.Collections.reverse(dailyDataList);
		}
			
		for(int i=0; i<dailyDataList.size(); i++) {
			
			today = dailyDataList.get(i);
			
			open = today.getOpen();
			//high = today.getHigh();
			//low = today.getLow();
			close = today.getClose();
			
			if(i == 0) { // first record - so price action is set based on just today's data.
				if(close >= open) {
					today.setPriceAction("G"); //green day
				}
				if(close < open) {
					today.setPriceAction("R"); //red day
				}
				yesterday = today;
			} else { // there is previous data available. so set price action based on prev day's data.
				
				//yesterday = dailyDataList.get(i-1);
				if( (close > open) && (close > yesterday.getClose())){ //today's open> today's close && today's close > y'days close.
					today.setPriceAction("G"); //green day
				} else if ((close > open) && (close < yesterday.getClose())){ //today's open> today's close && today's close > y'days close.
					today.setPriceAction("H"); //red hollow candle reversal day 
				} else if( (close < open) && (close > yesterday.getClose())) {
					today.setPriceAction("B"); //black or solid candle day as stock closed lower than opening price but above yesterday's close. 
				} else if ((close < open) && (close < yesterday.getClose()) ) {
					today.setPriceAction("R"); //red day
				}
				
				if( (close == open) && (close > yesterday.getClose())){ //today's open> today's close && today's close > y'days close.
					today.setPriceAction("G"); //green indecision day
				} else if ((close == open) && (close < yesterday.getClose())){ //today's open> today's close && today's close > y'days close.
					today.setPriceAction("R"); //red indecision day 
				} else if( (close > open) && (close == yesterday.getClose())) {
					today.setPriceAction("G"); //Green day as stock closed lower than opening price but above yesterday's close. 
				} else if ((close < open) && (close == yesterday.getClose()) ) {
					today.setPriceAction("R"); //red day
				}	else if ((close == open) && (close == yesterday.getClose()) ) {
					today.setPriceAction("G"); //neutral/green day
				}
				yesterday = today;
			}
			logger.debug(">>>>> Setting price action as :<<< "+today.getPriceAction()+" >>>>> for :"+stock.getSymbol()+" for ["+today.getDate()+"]");
		
		}
		//}
		java.util.Collections.reverse(dailyDataList);
		//persist price action changes..
		stockDao.addStock(stock);
		
		stockDao.flushAndClear();

	}
	

    private void printResults(List<StockDailyData> stockDailyList) {
 		for(StockDailyData s : stockDailyList ){
 			
 			String maValues = "[";
			for(MovingAverage ma : s.getMovingAverages()){
				maValues+=" "+ma.getPeriod()+ (ma.getType()==0?" SMA=":" EMA=")+ma.getMaValue();
			}
			maValues+="]";
			
			//String bbValues = "{ U:"+s.getBbUpper()+", M:"+s.getBbMiddle()+", L:"+s.getBbLower()+"}";
			
			logger.debug("["+s.getDate()+"] - "+maValues);
			//logger.debug("["+s.getDate()+"] - "+bbValues);
		}
	}
	
	public static void main(String[] args) {
		ScheduledTaskImpl impl = new ScheduledTaskImpl();
		impl.downloadHistoricalData(StockDataType.DAILY,null,null);
	}

	
}
